/*** JS OVERRIDES **************************************************************************************************/
String.prototype.ltrim=function(str){if(str)return this.replace(eval("/^"+str+"+/"),""); return this.replace(/^\s+/,"");};
String.prototype.rtrim=function(str){if(str)return this.replace(eval("/"+str+"+$/"),""); return this.replace(/\s+$/,"");};
String.prototype.trim=function(str){return this.ltrim(str).rtrim(str);};
String.prototype.clean=function(str){if(str)return this.replace(eval("/"+str+"+/g"),""); return this.replace(/\s+/,"");};
String.prototype.stripTags=function(){return this.replace(/<\/?\w+(\s*[^>])*>/g,'').replace(/</g,'').replace(/>/g,'');};
String.prototype.ucfirst=function(){return this.toLowerCase().replace(/\b([a-z])/gi,function(c){return c.toUpperCase();});};
String.prototype.decodeEntities=function(){try{var tmp=document.createElement("pre");tmp.innerHTML=this;return tmp.firstChild.nodeValue;}catch(e){return this.replace(/&amp;/g,'&');}};

Array.prototype.in_array=function(v){for(var i=0,l=this.length;i<l;i++)if(this[i]===v)return i;return -1;};
Array.prototype.remove=function(v){var nA=new Array();for(var i=0,l=this.length;i<l;i++)if(this[i]!=v)nA.push(this[i]);return nA;};
Array.prototype.removeIdx=function(idx){var nA=new Array();for(var i=0,l=this.length;i<l;i++)if(i!=idx)nA.push(this[i]);return nA;};
Array.prototype.max=function(){return Math.max.apply(null,this);};
Array.prototype.min=function(){return Math.min.apply(null,this);};
Array.prototype.unique=function(){var a=[],l=this.length,i=0,j;for(;i<l;i++){ for(j=i+1;j<l;j++)if(this[i]===this[j])j=++i;a.push(this[i]);}return a;};



/* DATE FUNCTIONS */
Date.prototype.months=[{shortName:'jan',days:31,name:'January'}, {shortName:'feb',days:28,name:'February'}, {shortName:'mar',days:31,name:'March'}, {shortName:'apr',days:30,name:'April'},{shortName:'may',days:31,name:'May'}, {shortName:'jun',days:30,name:'June'}, {shortName:'jul',days:31,name:'July'}, {shortName:'aug',days:31,name:'August'},{shortName:'sep',days:30,name:'September'}, {shortName:'oct',days:31,name:'October'}, {shortName:'nov',days:30,name:'November'}, {shortName:'dec',days:31,name:'December'}];

Date.prototype.addDays=function(n){var d=new Date(this);d.setDate(this.getDate()+(n||0));return d;};		// add n days to a date
Date.prototype.addMonths=function(n){var d=new Date(this); d.setMonth(d.getMonth()+(n||0),1); return d;};	// add n months to a date



//Returns ISO 8601 week number and year
Date.prototype.getFullWeek = function(){
    var jan1, w, d = new Date(this);
    d.setDate(d.getDate()+4-(d.getDay()||7));	// Set to nearest Thursday: current date + 4 - current day number, make Sunday's day number 7
    jan1 = new Date(d.getFullYear(),0,1);		// Get first day of year
    w = Math.ceil((((d-jan1)/86400000)+1)/7);	// Calculate full weeks to nearest Thursday
    return {y: d.getFullYear(), w: w };
};
//Returns ISO 8601 week number
Date.prototype.getWeek = function(){ return this.getFullWeek().w; };
var getWeeksInYear = function(y){ return new Date(y,11,28).getFullWeek().w; };

/**
 * Calculates the date for a Monday of the given week and year
 */
var getDateFromWeek = function(y, w){
    var dayN = 0, j10 = new Date(y,0,10), j4 = new Date(y,0,4), mon1 = +j4 - j10.getDay() * 86400000;
    return new Date(mon1 + ((w-1) * 7 + dayN) * 86400000);
};
var getStrDateFromWeek = function(y, w){ var d = getDateFromWeek(y,w); return d.getDate()+' '+d.months[d.getMonth()].shortName; };
var getShortStrDateFromWeek = function(y, w){ var d = getDateFromWeek(y,w); return d.getDate()+'/' + ('0'+(d.getMonth()+1)).slice(-2) ; };





/* unix time is calculated in miliseconds */
Date.prototype.toUnix=function(){return +this;};
Date.prototype.toStr=function(f){
	var str, Y=this.getFullYear(), M=this.getMonth(), D=this.getDate(), months=[{abbr:'jan',name:'January'},{abbr:'feb',name:'February'},{abbr:'mar',name:'March'},{abbr:'apr',name:'April'},{abbr:'may',name:'May'},{abbr:'jun',name:'June'},{abbr:'jul',name:'July'},{abbr:'aug',name:'August'},{abbr:'sep',name:'September'},{abbr:'oct',name:'October'},{abbr:'nov',name:'November'},{abbr:'dec',name:'December'}];
	if (f=='my') str=months[M].abbr+' '+Y;									// jan 2010
	else if (f=='d/m/y') str=D+'/'+(M+1)+'/'+Y;								// 21/12/2010
	else str=[Y,('0'+(M+1)).substr(-2),('0'+D).substr(-2)].join('-');		// 2010-12-21
	return str;
};

String.prototype.toDate=function(){var a=this.split('-');return new Date(a[0],a[1]-1,a[2]);};				// parse string date: 2011-01-31
String.prototype.toDate2=function(){var a=this.split('/');return new Date(a[2],a[1]-1,a[0]);};				// parse string date: 31/01/2011
String.prototype.toUnix=function(){return +this.toDate();};

Number.prototype.toDate=function(){return new Date(this);};
Number.prototype.toDateStr=function(){return this.toDate().toStr();};

function diffDate(d1,d2){ if(!d1||!d2)return 0; return Math.round((d1.toUnix()-d2.toUnix())/86400000);};	// substract dates in format: 2010-02-15 and return difference in days




/* OBJECT FUNCTIONS */

//object keys for old browsers
if(!Object.keys) Object.keys = function(o){ if (o !== Object(o)) throw new TypeError('Object.keys called on non-object'); var ret=[],p; for(p in o) if(Object.prototype.hasOwnProperty.call(o,p)) ret.push(p); return ret; };


/**
 * Compares 2 objects
 * @param x 	object 1
 * @param y 	object 2
 * @returns 	true if they are identical, false if they are different
 */
function compareObjects(x, y){
	for (var p in x){
		if(typeof(x[p]) !== typeof(y[p])) return false;
		if((x[p]===null) !== (y[p]===null)) return false;
		switch (typeof(x[p])) {
			case 'undefined': if (typeof(y[p]) != 'undefined') return false; break;
			case 'function': if (p != 'equals' && x[p].toString() != y[p].toString()) return false; break;
			case 'object': if(x[p]!==null && y[p]!==null && (x[p].constructor.toString() !== y[p].constructor.toString() || !x[p].equals(y[p]))) return false; break;
			default: if (x[p] !== y[p]) return false;
		}
	}
	return true;
};

/**
 * Cleans empty or null properties from an object
 * @param [required] 1			first parameter is an object to clean
 * @param [optional] 2,3,...	names of properties to remove from the object 
 * @returns						cleaned object
 */
function clearProperties(){
	if (arguments.length<1) return {};
	var obj = arguments[0], newO = {}, name, val, props = [];
	if (arguments.length>1) props = Array.prototype.slice.call(arguments,1);
	for (name in obj){ 
		val = obj[name]; 
		if (typeof val == 'undefined' || val == null || val == -1) continue;		// if null or undefined
		if (typeof val == 'string' && !val.length) continue;						// if empty string
		if (props.in_array(name)>-1) continue;										// if name is in disabled properties list 
		newO[name] = val; 
	}
	return newO;
};



/**
 * Queues ajax functions and runs them consecutively. e.g.: Loader.push(a); Loader.push(b); Loader.run();
 * Loader.run(); must be added in the ajax callback
 */
window.Loader = { queue: []
	,add: function(fn,scope,params){ this.queue.push(function(){ fn.apply(scope,params); Loader.run.call(Loader); }); }
	,push: function(fn,scope,params){ this.queue.push(function(){ if(typeof params=='undefined')fn.call(scope); else fn.apply(scope, params);}); }
	,run: function(){ if(this.queue.length) this.queue.shift().call(); }
};




/* GENERAL FUNCTIONS */
window.isTouch = navigator.userAgent.match(/iPhone|iPod|iPad/ig);


/**
 * jQuery-based serialization. Serializes a form to an object of { name:value }
 * @param form		jQuery form object
 * @param noEmpty	if true - return only non-empty values (0 is considered a non-empty value)
 * @returns 		js object: { name1: value1, ... }
 */
function serializeForm(form, noEmpty){
	var arr=form.serializeArray(),obj={},i=0,item;
	if (noEmpty){ for (; item=arr[i++] ;) if (typeof item.value !== 'undefined') obj[item.name] = item.value; }
	else for (; item=arr[i++] ;) obj[item.name] = item.value;
	return obj;
}


/**
 * Fills html form with data provided 
 * @param form		jQuery html object
 * @param data		object data
 */
function fillForm(form, data){
	var n,f;
	for (n in data){
		f=form.find('*[name="'+n+'"]');
		if(f.length){if(f[0].type=='radio'||f[0].type=='checkbox')f.val([data[n]]);else f.val(data[n]);}
	}
}

/**
 * Resets form fields
 * @param form		jQuery form object
 */
function resetForm(form){
	form.find('input:text,input[type=hidden],textarea,select').val('');
	form.find('input:radio,input:checkbox').removeAttr('checked');
}

/**
 * Formats numbers (or string numbers)
 * @param number	int or int-parsable string
 * @param prec		decimal precision
 * @returns			formatted number as string
 */
function numberFormat(number,prec){ // integer, float or string as input
	if (number == null || number == 'undefined') return 0;
	var num=0, sNum='', rgx = /(\d+)(\d{3})/;
	prec=prec||0;
	try{ num=number.toFixed(prec); sNum=num.toString(); } catch(e){ sNum=number; } // number to string
	var nArr=sNum.split('.'), name=nArr[0], ext=nArr[1];
	while (rgx.test(name)) name = name.replace(rgx, '$1'+','+'$2');
	return name+(ext?'.'+ext:'');
}

/**
 * Truncate text to the given length
 * @param text	string to truncate
 * @param len	required length
 * @returns		truncated text + ...
 */
function textTrunc(text,len){
	if (!text || !text.length) return;
	len = len||18;
	if (text.length>len-3) return text.substr(0,len-3)+'...';
	return text;
}


function rand(max,min){min=min||0;return Math.floor(Math.random()*(max-min)+min);};

/**
 * Return how many milisec elapsed from the given time
 * @param time
 */
function getElapsed(time){ if(typeof time=='object')time=+time; return +new Date()-time;};

/**
 * Returns the parameter value from address bar
 * @param param		parameter name
 * @param url		url string can be given
 * @returns			parameter value
 */
function getParam(param, url){
	var paramStr=(url || location.search).substr(1), paramAr=paramStr.split('&'), i=0, il=paramAr.length, par;
	for (; i<il; i++){par=paramAr[i].split('='); if(par[0]==param)return par[1];}
	return false;	
};


function log(){try{console.log.apply(this,arguments);}catch(e){}}
function dir(){try{console.dir.apply(this,arguments);}catch(e){}}
function error(){try{console.error.apply(this,arguments);}catch(e){}return false;}
function die(){try{console.warn.apply(this,arguments);}catch(e){}return false;}
